﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Word_Mojo
{
    class BoardAnalyzer
    {
        private Tile[,] theGameBoard;
        private Tile[,] theGameBoardPlay;
        
        private int pointsForThePlay;

        private Dictionary<GamePlay, bool> gamePlays;

        public BoardAnalyzer()
        {
            this.theGameBoard = new Tile[9, 9];
            this.theGameBoardPlay = new Tile[9, 9];
            this.gamePlays = new Dictionary<GamePlay, bool>();
            this.pointsForThePlay = 0;
        }

        /// <summary>
        /// Submit the play made
        /// </summary>
        /// <param name="gameboardPlay"></param>
        /// <returns>The points for the play, returns zero </returns>
        public int submitPlayToGetPointValue(Tile[,] gameboardPlay)
        {
            theGameBoardPlay = gameboardPlay;
            this.gamePlays.Clear();
            this.pointsForThePlay = 0;
            
            this.findHorizontalPlays();
            this.findVerticalPlays();
            this.verifyPlaysToCalculatePoints();

            return this.pointsForThePlay;
        }

        public Tile[,] getGameBoard()
        {
            return this.theGameBoard;
        }

        /// <summary>
        /// Returns a List of String objects, the string objects represent playable tiles.  The String uses * to represent
        /// the part of the board open for play.  
        /// 
        /// i.e. the list may contain a string as follows:  "***s*" which means there is a tile 's' on the board where 
        /// there are three open spaces in front/above and one empty space after/below
        /// </summary>
        /// <returns>List of String objects, the string objects represent playable tiles</returns>
        public List<String> getPlayableTileStructures()
        {
            List<String> playableTiles = new List<String>();

            int freeSpacesAbove, freeSpacesBelow, freeSpacesLeft, freeSpacesRight;

            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    //this code is terrible, needs refactoring
                    freeSpacesAbove = this.findOpenTilesLength(i, j, "up");
                    freeSpacesBelow = this.findOpenTilesLength(i, j, "down");
                    freeSpacesLeft = this.findOpenTilesLength(i, j, "left");
                    freeSpacesRight = this.findOpenTilesLength(i, j,"right");

                    if (freeSpacesRight + freeSpacesLeft >= 5)
                    {
                        String playableTile = this.theGameBoard[i,j].Letter;

                        for (int x = 0; x < freeSpacesLeft; x++)
                        {
                            playableTile = "*" + playableTile;
                        }
                        for (int x = 0; x < freeSpacesRight; x++)
                        {
                            playableTile = playableTile + "*";
                        }

                        playableTiles.Add(playableTile);
                    }

                    if (freeSpacesAbove + freeSpacesBelow >= 5)
                    {
                        String playableTile = this.theGameBoard[i, j].Letter;

                        for (int x = 0; x < freeSpacesAbove; x++)
                        {
                            playableTile = "*" + playableTile;
                        }
                        for (int x = 0; x < freeSpacesBelow; x++)
                        {
                            playableTile = playableTile + "*";
                        }

                        playableTiles.Add(playableTile);
                    }

                }
            }

            return playableTiles;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameBoard"></param>
        /// <returns></returns>
        public List<GamePlay> GetGamePlaysOnBoard(Tile[,] gameBoard)
        {
            submitPlayToGetPointValue(gameBoard);

            List<GamePlay> gamePlays = new List<GamePlay>();

            foreach (GamePlay play in this.gamePlays.Keys)
            {
                gamePlays.Add(play);
            }

            return gamePlays;
        }

        #region Private Helpers

        private void findHorizontalPlays()
        {
            Tile curr;
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    curr = this.theGameBoardPlay[i, j];
                    if (curr.Letter != "")
                    {
                        j = this.createNewHorizontalPlay(curr, i, j);
                    }
                }
            } 
        }

        private int createNewHorizontalPlay(Tile aTile, int arrayIndexStart, int entryIndexStart)
        {
            int i = entryIndexStart;

            List<Tuple<int, int, Tile>> horizontalTiles = new List<Tuple<int,int,Tile>>();
            Tuple<int, int, Tile> curr;

            while (i < 9 && this.theGameBoardPlay[arrayIndexStart, i].Letter != "")
            {
                curr = new Tuple<int,int,Tile>(arrayIndexStart, i, theGameBoardPlay[arrayIndexStart, i]);
                horizontalTiles.Add(curr);
                i++;
            }

            if (horizontalTiles.Count >= 3)
            {
                this.gamePlays.Add(new GamePlay(horizontalTiles), false);
            }
            //should return i-1
            return i;
        }

        private void findVerticalPlays()
        {
            Tile curr;
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    curr = this.theGameBoardPlay[j, i];
                    if (curr.Letter != "")
                    {
                        j = this.createNewVerticalPlay(curr, j, i);
                    }
                }
            } 
        }

        private int createNewVerticalPlay(Tile aTile, int arrayIndexStart, int entryIndexStart)
        {
            int i = arrayIndexStart;

            List<Tuple<int, int, Tile>> verticalTiles = new List<Tuple<int, int, Tile>>();
            Tuple<int, int, Tile> curr;

            while (i < 9 && this.theGameBoardPlay[i, entryIndexStart].Letter != "")
            {
                curr = new Tuple<int, int, Tile>(i, entryIndexStart, theGameBoardPlay[i, entryIndexStart]);
                verticalTiles.Add(curr);
                i++;
            }

            if (verticalTiles.Count >= 3)
            {
                this.gamePlays.Add(new GamePlay(verticalTiles), false);
            }

            return i;
        }

        private void verifyPlaysToCalculatePoints()
        {
            foreach (GamePlay curr in this.gamePlays.Keys)
            {
                //check if the play is valid
                if (curr.getPoints() != 0)
                {
                    //play is valid play
                    this.addThePlayToTheGameBoard(curr);
                    this.pointsForThePlay += curr.getPoints();
                }
            }
        }

        private void addThePlayToTheGameBoard(GamePlay curr)
        {
            foreach (Tuple<int, int, Tile> t in curr.getTiles())
            {
                this.theGameBoard[t.Item1, t.Item2] = t.Item3; 
            }
        }

        private int findOpenTilesLength(int arrayIndexStart, int entryIndexStart, String direction)
        {
            int numberOfOpenSpaces = 0;

            switch (direction)
            {
                case "right":
                    if (this.theGameBoard[arrayIndexStart, entryIndexStart - 1].Letter != "")
                    {
                        break; 
                    }
                    for (;  entryIndexStart <= 8; entryIndexStart++)
                    {
                        if (this.theGameBoard[arrayIndexStart, entryIndexStart].Letter == "")
                        {
                            numberOfOpenSpaces++;
                        }
                    }
                    break;
                case "left":
                    if (this.theGameBoard[arrayIndexStart, entryIndexStart + 1].Letter != "")
                    {
                        break;
                    }
                    for (; entryIndexStart >= 0; entryIndexStart--)
                    {
                        if (this.theGameBoard[arrayIndexStart, entryIndexStart].Letter == "")
                        {
                            numberOfOpenSpaces++;
                        }
                    }
                    break;
                case "up":
                    if (this.theGameBoard[arrayIndexStart + 1, entryIndexStart].Letter != "")
                    {
                        break;
                    }
                    for (; arrayIndexStart <= 8; arrayIndexStart++)
                    {
                        if (this.theGameBoard[arrayIndexStart, entryIndexStart].Letter == "")
                        {
                            numberOfOpenSpaces++;
                        }
                    }
                    break;
                case "down":
                    if (this.theGameBoard[arrayIndexStart - 1, entryIndexStart].Letter != "")
                    {
                        break;
                    }
                    for (; arrayIndexStart >= 0; arrayIndexStart--)
                    {
                        if (this.theGameBoard[arrayIndexStart, entryIndexStart].Letter == "")
                        {
                            numberOfOpenSpaces++;
                        }
                    }
                    break;
                default:
                    return 0;
            }

            return numberOfOpenSpaces;
        }

        #endregion
    }
}